package crawler;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import org.htmlparser.Parser;
import org.htmlparser.Node;
import org.htmlparser.Text;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.Tag;

public class Extractor {
	private static int MAX_LEVEL = 100;
	private ArrayList<ExtractedInfo> extractedInfo;
	
	public Extractor()
	{
		extractedInfo = new ArrayList<ExtractedInfo>();
	}

	public void Extract(CrawlerDocument doc) throws ParserException{
		String content = (String)doc.getFeature("content");
		
		Parser parser = new Parser(content);
		NodeList nodes = parser.parse(null);
		
		this.extractedInfo.clear();
		recursiveExtract(nodes, 0);
		
		URL seedurl = doc.getUrl();
		String seedhost = seedurl.getHost();
		
		for(int i = 0; i < extractedInfo.size(); ++i){
			ExtractedInfo info = extractedInfo.get(i);
			
			String url = (String)info.getFeature("link");
			if (!url.startsWith("http")){
				if (url.startsWith("/")){
					url = url.substring(1);
				}
				url = "http://" + seedhost + "/" + url;
				info.setFeature("link", url);
			}
			
			try{
				CrawlerDocument outlink = new CrawlerDocument(url);
				outlink.setFeatures(info.getFeatures());
				doc.addOutlinkDoc(outlink);
			}catch(MalformedURLException e){
				e.printStackTrace();
				// TODO : LOG AS WARNING
			}
						
		}
	}
	
	private ArrayList<Node> recursiveExtract(NodeList nodes, int level)
	{
		if (level > MAX_LEVEL) return null;
		
		ArrayList<Node> currRet = new ArrayList<Node>();		
		
		for(int i = 0; i < nodes.size(); ++i){
			Node node = nodes.elementAt(i);
			
			if (node instanceof Tag){
				NodeList children = node.getChildren();
				ArrayList<Node> childRet = null;
				
				// Recursive DFS
				if (children != null){					
					childRet = recursiveExtract(children, level + 1);
				}
				
				// Merge returned nodes of all children's children
				if (childRet != null){
					for (int x = 0; x < childRet.size(); ++x){
						Node retNode = childRet.get(x);
						currRet.add(retNode);
					}
				}

				// Judge current node : img or a
				if (((Tag) node).getTagName().equalsIgnoreCase("img") ||
					((Tag) node).getTagName().equalsIgnoreCase("a")){
					currRet.add(node);
				}
				
			}
			
			// Judge current node : price
			if(isPriceNode(node)){
				currRet.add(node);
			}
		}
		
		if (isWanted(currRet)){
			ExtractedInfo info = extractWantedInfo(currRet);
			this.extractedInfo.add(info);
			currRet.clear();
		}
		return currRet;
	}	
	
	private String extractText(Node node, int depth){
		if (0 == depth){
			return "";
		}
		
		if (node instanceof Text){
			Text textNode = (Text)node;
			return textNode.getText();		
		}
		
		NodeList children = node.getChildren();
		if (children != null){
			String text = "";
			for (int i = 0; i < children.size(); ++i){
				text += extractText(children.elementAt(i), depth - 1);
			}
			return text;
		}
		
		return "";
	}
	
	private double extractPrice(Node node){
		String text = extractText(node, 5);
		String target = "";
		for (int i = 0; i < text.length(); ++i){
			char c = text.charAt(i);
			if ('.' == c || (c >= '0' && c <= '9')){
				target += c;
			}
		}
		
		double ret = 0;
		try{
			ret = Double.parseDouble(target);
		}catch (Exception e){
			ret = -1;
		}
		
		return ret;
	}
	
	private boolean isPriceNode(Node node){
		String text = extractText(node, 3);
		if (text.indexOf("¥") >= 0 && text.length() > 1){
			return true;
		}else{
			return false;
		}
		
	}

	private boolean isWanted(ArrayList<Node> nodelist){
		if(nodelist.size() < 3) return false;
		boolean hasA = false;
		boolean hasImg = false;
		boolean hasPirce = false;
		for (int i = 0; i < nodelist.size(); ++i){
			Node node = nodelist.get(i);
			if (node instanceof Tag){
				if (((Tag)node).getTagName().equalsIgnoreCase("a")){
					hasA = true;
				}
				if (((Tag)node).getTagName().equalsIgnoreCase("img")){
					hasImg = true;
				}
				if (isPriceNode(node)){
					
					hasPirce = true;
				}
			}
		}
		if (hasA && hasImg && hasPirce){
			return true;
		}else{
			return false;
		}
	}
	
	private ExtractedInfo extractWantedInfo(ArrayList<Node> nodelist){
		ExtractedInfo info = new ExtractedInfo();
		
		for(int i = 0; i < nodelist.size(); ++i){
			Node node = nodelist.get(i);
			if (isPriceNode(node)){
				double price = extractPrice(node);
				info.setFeature("price", price);
			}
			else if(node instanceof Tag){
				Tag tagNode = (Tag)node;
				
				if(tagNode.getTagName().equalsIgnoreCase("img") && 
				   tagNode.getAttribute("alt") != null){
					String image = tagNode.getAttribute("imgsrc");
					info.setFeature("image", image);
				}else{
					String text = extractText(node, 3);
					text = text.trim();
					if (text.length() > 0){
						info.setFeature("text", text);
						String link = tagNode.getAttribute("href");
						info.setFeature("link", link);
					}
				}
			}
			
			
		}
		return info;
	}
}
